﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

using Microsoft.WowAddonStudio.Lua.Model;
using Microsoft.WowAddonStudio.Lua.Parser;
using Microsoft.WowAddonStudio.Lua.Support;

namespace Microsoft.WowAddonStudio.Lua.AST
{
	/// <summary>
	/// A node in the abstract syntax tree parsed by the Lua Language Service.
	/// </summary>
	public class Node : IEnumerable<Node>
	{
		private readonly LexLocation location;

		/// <summary>
		/// Initializes a new instance of the <see cref="Node"/> class.
		/// </summary>
		/// <param name="location">The location of the node.</param>
		public Node(LexLocation location)
		{
			this.location = location;
		}

		/// <summary>
		/// Gets the location of the node in the parsed file.
		/// </summary>
		/// <value>The location.</value>
		public LexLocation Location
		{
			get { return location; }
		}

        /// <summary>
        /// Gets or sets the scope of the node.
        /// </summary>
        public Scope Scope { get; set; } 

		/// <summary>
		/// Gets or sets the next sibling node.
		/// </summary>
		/// <value>The next.</value>
		public Node Next { get; set; }

		/// <summary>
		/// Gets the child nodes of the node.
		/// </summary>
		/// <returns>An enumerable collection of nodes.</returns>
		public virtual IEnumerable<Node> GetChildNodes()
		{
			yield break;
		}

        /// <summary>
        /// Returns the node at the given location.
        /// </summary>
        /// <param name="line">The index of the line.</param>
        /// <param name="column">The index of the column.</param>
        /// <returns>An instance of the <see cref="Node"/> class, if found; null otherwise.</returns>
        public Node GetNodeAt(int line, int column)
        {
            // Iterate through the children and find a node that contains the location
            foreach (var node in this.GetChildNodes())
            {
                if (node.Location.Contains(line, column))
                    return node.GetNodeAt(line, column);
            }

            // No children node was found, see if we contain the location
            if (this.Location.Contains(line, column))
                return this;

            return null;
        }

	    /// <summary>
		/// Writes a dump of the node tree using Trace.
		/// </summary>
		public void WriteTraceDump()
		{
			// Dump ourselves, write location information, if we have any
			if (location != null)
				Trace.WriteLine(String.Format("{0} (Line: {1}, Column: {2}, EndLine: {3}, EndColumn: {4})", this, location.Line,
				                              location.Column, location.EndLine, location.EndColumn));
			else
				Trace.WriteLine(this);

			// Indent and dump the child nodes
			Trace.Indent();

			foreach (var node in GetChildNodes())
			{
				if (node != null)
					node.WriteTraceDump();
			}

			// Unindent back
			Trace.Unindent();

			// Dump the next node
			if (Next != null)
				Next.WriteTraceDump();
		}

		/// <summary>
		/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
		/// </returns>
		public override string ToString()
		{
			return this.GetType().Name;
		}

		/// <summary>
		/// Returns an enumerator that iterates through the collection.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
		/// </returns>
		public IEnumerator<Node> GetEnumerator()
		{
			Node node = this;

			while (node != null)
			{
				yield return node;
				node = node.Next;
			}
		}

		/// <summary>
		/// Returns an enumerator that iterates through a collection.
		/// </summary>
		/// <returns>
		/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
		/// </returns>
		IEnumerator IEnumerable.GetEnumerator()
		{
			return this.GetEnumerator();
		}

        /// <summary>
        /// Returns the nodes as a linked list.
        /// </summary>
        /// <param name="nodes">The nodes to link.</param>
        /// <returns></returns>
        public static TNode LinkedList<TNode>(params TNode[] nodes) where TNode : Node
        {
            for (int i = 0; i < nodes.Length - 1; i++)
                nodes[i].Next = nodes[i + 1];

            nodes[nodes.Length - 1].Next = null;

            return nodes[0];
        }

	}
}